package cn.com.idmy.cloud.config;

import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Data
@Slf4j
class DruidPoolConfig {
    // 连接池基础配置
    private int maxActive;                          // 最大连接数
    private int initialSize;                        // 初始连接数
    private int minIdle;                            // 最小空闲连接
    private long maxWait;                           // 获取连接超时时间（毫秒）
    private int notFullTimeoutRetryCount = 1;       // 连接池满时重试次数

    // 空闲连接回收配置
    private long timeBetweenEvictionRunsMillis;     // 检测间隔时间
    private long minEvictableIdleTimeMillis;        // 最小空闲时间
    private long maxEvictableIdleTimeMillis;        // 最大空闲时间
    private boolean keepAlive = true;               // 是否启用连接保活
    private long keepAliveBetweenTimeMillis;        // 保活间隔时间

    // 连接检测配置
    private String validationQuery = "SELECT 1";    // 连接检测SQL
    private boolean testWhileIdle = true;           // 空闲时检测
    private boolean testOnBorrow = true;            // 借用时检测
    private boolean testOnReturn = false;           // 归还时检测

    // 连接泄露检测
    private int removeAbandonedTimeout;             // 泄露检测超时时间（秒）
    private boolean logAbandoned = true;            // 是否记录泄露日志

    // PreparedStatement池配置
    private boolean poolPreparedStatements = true;  // 是否启用PS池
    private int maxPoolPreparedStatementPerConnectionSize; // 每连接PS池大小
    private boolean sharePreparedStatements = false; // 是否共享PS

    // 同步初始化配置
    private boolean asyncInit = true;              // 是否异步初始化
    private int maxCreateTaskCount = 10;            // 最大创建任务数

    // 查询超时配置
    private int queryTimeout;                       // 普通查询超时（秒）
    private int transactionQueryTimeout;            // 事务查询超时（秒）

    // 监控配置
    private long slowSqlMillis = 2000;              // 慢SQL阈值（毫秒）

    @Data
    @Builder
    public static class DruidProfile {
        private int qps;                 // 预期 QPS (每秒查询数)
        private int avgQueryTime;        // 平均查询时间（毫秒）
        private int maxQueryTime;        // 最大查询时间（毫秒）
        private double peakFactor;         // 峰值因子（峰值QPS/平均QPS）
        @Builder.Default
        private int cpuCores = Runtime.getRuntime().availableProcessors(); // CPU 核心数
        private int dbMaxConnections;      // 数据库最大连接数
        private boolean isReadHeavy;       // 是否读多写少
        private boolean hasLongTransaction; // 是否有长事务

        public static DruidProfile of(String profile) {
            return switch (profile) {
                case "query" -> DruidProfile.query();
                case "batch" -> DruidProfile.batch();
                case "reporting" -> DruidProfile.reporting();
                case "local" -> DruidProfile.local();
                default -> DruidProfile.defaultProfile();
            };
        }

        public static DruidProfile defaultProfile() {
            return DruidProfile.builder()
                    .qps(100)
                    .avgQueryTime(150)
                    .maxQueryTime(10000)
                    .peakFactor(3.0)
                    .cpuCores(Runtime.getRuntime().availableProcessors())
                    .dbMaxConnections(2048)
                    .isReadHeavy(false)
                    .hasLongTransaction(true)
                    .build();
        }

        public static DruidProfile query() {
            return DruidProfile.builder()
                    .qps(50)
                    .avgQueryTime(100)
                    .maxQueryTime(5000)
                    .peakFactor(2.0)
                    .cpuCores(Runtime.getRuntime().availableProcessors())
                    .dbMaxConnections(2048)
                    .isReadHeavy(true)
                    .hasLongTransaction(false)
                    .build();
        }

        public static DruidProfile batch() {
            return DruidProfile.builder()
                    .qps(50)
                    .avgQueryTime(500)
                    .maxQueryTime(30000)
                    .peakFactor(1.5)
                    .dbMaxConnections(2048)
                    .hasLongTransaction(true)
                    .build();
        }

        public static DruidProfile reporting() {
            return DruidProfile.builder()
                    .qps(20)
                    .avgQueryTime(2000)
                    .maxQueryTime(60000)
                    .peakFactor(2.0)
                    .dbMaxConnections(2048)
                    .isReadHeavy(true)
                    .build();
        }

        public static DruidProfile local() {
            return DruidProfile.builder()
                    .qps(5) // 开发环境QPS较低
                    .avgQueryTime(100)  // 平均查询100ms（包含调试）
                    .maxQueryTime(60000) // 最大60秒（断点调试）
                    .peakFactor(1.0)
                    .dbMaxConnections(2048) // 数据库通常配置较高
                    .hasLongTransaction(true) // 可能有长事务（调试）
                    .build();
        }
    }

    static DruidPoolConfig calculate(DruidProfile profile) {
        var cfg = new DruidPoolConfig();

        // 1. 使用 w 计算基础连接数
        // 连接数 = QPS × 平均响应时间(秒)
        var avgResponseTimeSec = profile.avgQueryTime / 1000.0;
        var baseConnections = (int) Math.ceil(profile.qps * avgResponseTimeSec);

        // 2. 考虑峰值情况
        var peakConnections = (int) Math.ceil(baseConnections * profile.peakFactor);

        // 3. 使用 HikariCP 推荐公式作为参考
        // 连接数 = (CPU核心数 × 2) + 有效磁盘数
        // 对于 SSD，有效磁盘数通常为 1
        var recommended = profile.cpuCores * 2 + 1;

        // 4. 综合计算最大连接数
        int maxActive;
        if (profile.isReadHeavy) {
            // 读多写少场景，可以有更多连接
            maxActive = Math.max(peakConnections, recommended);
            // 但不超过 CPU 核心数的 10 倍
            maxActive = Math.min(maxActive, profile.cpuCores * 10);
        } else {
            // 写多场景，限制连接数避免锁竞争
            maxActive = Math.min(peakConnections, recommended * 2);
        }

        // 5. 考虑数据库最大连接数限制（预留 20% 给其他应用）
        maxActive = Math.min(maxActive, (int) (profile.dbMaxConnections * 0.4));

        // 6. 设置合理的边界
        maxActive = Math.max(maxActive, 5);  // 最少 10 个连接
        maxActive = Math.min(maxActive, 200); // 最多 200 个连接

        // 7. 计算初始连接数（基于平均负载）
        // 初始连接数应该能满足正常负载，避免启动时大量创建连接
        var initialSize = Math.max(baseConnections, 5);
        // 但不超过最大连接数的 30%（原来是50%，过高）
        initialSize = Math.min(initialSize, (int) (maxActive * 0.3));
        // 确保至少有一定数量的初始连接
        initialSize = Math.max(initialSize, Math.min(5, maxActive));

        // 8. 最小空闲连接（保持一定的热连接）
        // 不再强制使用 hikariRecommended，而是基于基础连接数
        var minIdle = Math.max(baseConnections / 2, 5);
        // 确保不超过初始连接数
        minIdle = Math.min(minIdle, initialSize);
        // 不超过最大连接数的 25%
        minIdle = Math.min(minIdle, (int) (maxActive * 0.25));

        // 9. 获取连接超时时间
        // 基于平均查询时间，给予合理的等待时间
        long maxWait;
        if (profile.avgQueryTime < 100) {
            maxWait = 2000;  // 快速查询，2秒超时
        } else if (profile.avgQueryTime < 500) {
            maxWait = 5000;  // 中等查询，5秒超时
        } else {
            maxWait = 10000; // 慢查询，10秒超时
        }

        // 10. PreparedStatement 池大小
        // 基于常用 SQL 数量，通常 20-50 个
        int psPoolSize;
        if (profile.isReadHeavy) {
            psPoolSize = 50;  // 读多场景，可能有更多不同的查询
        } else {
            psPoolSize = 30;  // 写多场景，SQL 相对固定
        }

        // 查询超时配置
        // 基于最大查询时间，给予 20% 缓冲
        var queryTimeout = (int) Math.ceil(profile.maxQueryTime / 1000.0 * 1.2);
        queryTimeout = Math.max(queryTimeout, 5);  // 至少 5 秒
        queryTimeout = Math.min(queryTimeout, 120); // 最多 120 秒（原60秒对报表可能不够）

        // 事务查询超时（考虑多个查询的组合）
        var transactionQueryTimeout = queryTimeout * 3;
        if (profile.hasLongTransaction) {
            transactionQueryTimeout = Math.max(transactionQueryTimeout, 180);
        }

        // 连接泄露检测
        // 基于最大查询时间的 2-3 倍
        var removeAbandonedTimeout = (int) Math.ceil(profile.maxQueryTime / 1000.0 * 2);
        removeAbandonedTimeout = Math.max(removeAbandonedTimeout, 60);  // 至少 1 分钟
        removeAbandonedTimeout = Math.min(removeAbandonedTimeout, 600); // 最多 10 分钟

        // 空闲连接回收策略
        // 检测间隔：30-60 秒
        var timeBetweenEvictionRunsMillis = 60000;
        // 最小空闲时间：基于平均查询间隔
        var minEvictableIdleTimeMillis = Math.max(300000, profile.avgQueryTime * 100);
        // 最大空闲时间：15-30 分钟
        var maxEvictableIdleTimeMillis = 900000;

        // 设置配置值
        cfg.setMaxActive(maxActive);
        cfg.setInitialSize(initialSize);
        cfg.setMinIdle(minIdle);
        cfg.setMaxWait(maxWait);
        cfg.setNotFullTimeoutRetryCount(1);  // 重试 2 次

        // 空闲连接回收配置
        cfg.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        cfg.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        cfg.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
        cfg.setKeepAlive(true);
        cfg.setKeepAliveBetweenTimeMillis(120000);  // 2 分钟保活

        // 连接检测配置
        cfg.setValidationQuery("SELECT 1");
        cfg.setTestWhileIdle(true);
        cfg.setTestOnBorrow(false);  // 避免每次获取连接都检测，影响性能
        cfg.setTestOnReturn(false);

        // 连接泄露检测
        cfg.setRemoveAbandonedTimeout(removeAbandonedTimeout);
        cfg.setLogAbandoned(true);

        // PreparedStatement 池配置
        cfg.setPoolPreparedStatements(true);
        cfg.setMaxPoolPreparedStatementPerConnectionSize(psPoolSize);
        cfg.setSharePreparedStatements(false);

        // 同步初始化
        cfg.setAsyncInit(false);
        cfg.setMaxCreateTaskCount(profile.cpuCores);

        // 查询超时配置
        cfg.setQueryTimeout(queryTimeout);
        cfg.setTransactionQueryTimeout(transactionQueryTimeout);

        // 慢 SQL 阈值（平均时间的 10 倍或 1 秒，取较大值）
        cfg.setSlowSqlMillis(Math.max(1000, profile.avgQueryTime * 10));
        return cfg;
    }
}